package qinghua

import (
	"bytes"
	"fmt"
	"runtime"
	"strconv"
	"sync"
)

// goroutineSpace 是goroutine堆栈信息的前缀
var goroutineSpace = []byte("goroutine ")

// curGoroutineID 获取当前goroutine的ID
func curGoroutineID() uint64 {
	// 获取当前goroutine的堆栈信息
	b := make([]byte, 64)
	b = b[:runtime.Stack(b, false)]
	// 去除前缀，并解析出goroutine的ID
	b = bytes.TrimPrefix(b, goroutineSpace)
	i := bytes.IndexByte(b, ' ')
	if i < 0 {
		panic(fmt.Sprintf("在 %q 中未找到空格", b))
	}
	b = b[:i]
	n, err := strconv.ParseUint(string(b), 10, 64)
	if err != nil {
		panic(fmt.Sprintf("从 %q 中解析goroutine ID失败: %v", b, err))
	}
	return n
}

// printTrace 打印跟踪信息
func printTrace(id uint64, name string, line int, arrow string, indent int) {
	// 生成缩进字符串
	indents := ""
	for i := 0; i < indent; i++ {
		indents += "    "
	}
	// 打印跟踪信息，使用ANSI颜色代码为关键信息着色
	fmt.Printf("\033[1;32mg[%05d]\033[0m:%s%s\033[1;32m%s\033[0m:%d\n", id, indents, arrow, name, line)
}

// mu 互斥锁，用于保护map的并发访问
var mu sync.Mutex

// m 存储每个goroutine的缩进级别
var m = make(map[uint64]int)

// Trace 返回一个函数，用于跟踪goroutine的进入和退出
func Trace() func() {
	pc, _, line, ok := runtime.Caller(1)
	if !ok {
		panic("未找到调用者")
	}

	fn := runtime.FuncForPC(pc)
	name := fn.Name()
	gid := curGoroutineID()

	mu.Lock()
	indents := m[gid]
	m[gid] = indents + 1
	mu.Unlock()
	printTrace(gid, name, line, "->", indents+1)

	return func() {
		mu.Lock()
		indents := m[gid]
		m[gid] = indents - 1
		mu.Unlock()
		printTrace(gid, name, line, "<-", indents)
	}
}
